﻿using System;
using System.ComponentModel;
using System.Net;
using Utils;

namespace StockAccessors.Interdin.Engine
{
	internal class AccessorEngine : MessageProcessor, INotifyPropertyChanged
	{
		#region Attributes

		private readonly FuturesPageReader _futurePageReader;
		private readonly CfdPageReader _cfdPageReader;
		private readonly ConnectionManager _connectionManager;

		private bool _connected;
		private bool _connectedAtLeastOnce;
		private int _reconnections;
		private bool _invalidConnectionData;

		private bool _stopping;

		#endregion

		#region Properties

		public int Reconnections
		{
			get
			{
				return _reconnections;
			}
			private set
			{
				if (_reconnections != value)
				{
					_reconnections = value;
					this.OnPropertyChanged("Reconnections");
				}
			}
		}

		public bool Connected
		{
			get
			{
				return _connected;
			}
			private set
			{
				if (_connected != value)
				{
					if (value)
					{
						_connectedAtLeastOnce = true;
					}

					_connected = value;
					this.OnPropertyChanged("Connected");
				}
			}
		}

		public bool InvalidConnectionData
		{
			get
			{
				return _invalidConnectionData;
			}
			set
			{
				if (_invalidConnectionData != value)
				{
					_invalidConnectionData = value;
					this.OnPropertyChanged("InvalidConnectionData");
				}
			}
		}

		#endregion

		#region Public Methods

		public AccessorEngine(ConnectionManager connectionManager, CfdPageReader cfdPageReader, FuturesPageReader futurePageReader) : base("Accessor Engine Message Processor")
		{
			_connectionManager = connectionManager;
			_cfdPageReader = cfdPageReader;
			_futurePageReader = futurePageReader;
		}

		public void AddOrder(AccessorMessageType message)
		{
			base.Messages.Add(new Message(message));
		}

		public override void Start()
		{
			_stopping = false;
			base.Start();
		}
		public override void Stop()
		{
			_stopping = true;
			base.Stop();
		}

		protected override void MainMethod()
		{
			base.MainMethod();

			_cfdPageReader.Dispose();
			_futurePageReader.Dispose();
		}

		protected override void ProcessMessage()
		{
			if (base.CurrentMessage.Type == AccessorMessageType.Connect)
			{
				this.Connect();
			}
			else if (base.CurrentMessage.Type == AccessorMessageType.ReadOnce)
			{
				this.ReadOnce();
			}
			else if (base.CurrentMessage.Type == AccessorMessageType.Disconnect)
			{
				this.Disconnect();
			}
			else if (base.CurrentMessage.Type == AccessorMessageType.Reconnect)
			{
				if (this.Connected)
				{
					this.Reconnect();
				}
			}
		}

		#endregion

		#region Private Methods

		private void Connect()
		{
			this.InternalConnect(false);
		}

		private void ReadOnce()
		{
			this.InternalConnect(true);
		}

		private void Disconnect()
		{
			try
			{
				_futurePageReader.Stop();
				_cfdPageReader.Stop();
				_connectionManager.Disconnect();
			}
			catch (Exception ex)
			{
				// Ignore exceptions when disconnecting
				ExceptionLogger.LogException("Exception received while disconnecting. Ignored.", ex, false);
			}
			finally
			{
				this.Connected = false;
			}
		}

		private void Reconnect()
		{
			this.Disconnect();
			this.Connect();
		}

		private void InternalConnect(bool readOnce)
		{
			while (!this.Connected && !_stopping)
			{
				try
				{
					this.Reconnections++;

					_connectionManager.Connect();
					_futurePageReader.ContractNumber = _connectionManager.ContractNumber;
					_cfdPageReader.ContractNumber = _connectionManager.ContractNumber;
					_futurePageReader.Cookies = _connectionManager.Cookies;
					_cfdPageReader.Cookies = _connectionManager.Cookies;

					if (readOnce)
					{
						_futurePageReader.ReadOnce();
						_cfdPageReader.ReadOnce();
					}
					else
					{
						_futurePageReader.Start();
						_cfdPageReader.Start();
					}

					this.Connected = true;
				}
				catch (Exception ex)
				{
					if (ex is InvalidConnectionDataException && !_connectedAtLeastOnce)
					{
						ExceptionLogger.LogException("Exception received while connecting.", ex, true);
						this.InvalidConnectionData = true;
						_stopping = true;
					}
					else
					{
						ExceptionLogger.LogException("Exception received while connecting.", ex, false);
						WebException webException = ex as WebException;
						if (ex is InvalidConnectionDataException || (webException != null && webException.Response != null && ((HttpWebResponse)webException.Response).StatusCode == HttpStatusCode.InternalServerError))
						{
							System.Threading.Thread.Sleep(Properties.Settings.Default.ReconnectionWait);
						}
						this.Disconnect();
					}
				}
			}
		}

		#endregion

		#region INotifyPropertyChanged Members

		[field: NonSerialized]
		public event PropertyChangedEventHandler PropertyChanged;

		protected virtual void OnPropertyChanged(string propertyName)
		{
			if (this.PropertyChanged != null)
			{
				this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
			}
		}

		#endregion

		#region IDisposable

		protected override void InternalDispose(bool disposing)
		{
			if (disposing)
			{
				_cfdPageReader.Dispose();
				_futurePageReader.Dispose();
			}

			base.InternalDispose(disposing);
		}

		#endregion
	}
}
